import pandas as pd
import numpy as np
from sklearn.decomposition import PCA

def pca_weight(
    df: pd.DataFrame,
    columns: list,
    n_components: int | None = None
) -> pd.Series:
    """
    使用主成分分析计算指标权重
    
    参数:
        df: 带时间序列数据的DataFrame
        columns: 包含时间序列的列名列表
        n_components: 需要保留的主成分数量，默认为None(保留所有主成分)
        
    返回:
        pd.Series: 索引为列名，值为PCA计算的权重
        
    注意:
        - 输入数据应仅包含参与PCA的特征列
        - NaN值会被自动剔除
        - 建议对数据进行标准化处理后使用此函数
        - 如果n_components=0，将返回全零权重
    """
    # 输入验证处理
    if not isinstance(columns, (list, tuple)):
        raise TypeError(f"columns参数必须是列表或元组，但得到：{type(columns)}")
    
    if len(columns) < 2:
        raise ValueError("PCA至少需要两个维度，但columns少于2个")
    
    # 数据预处理
    data = df[columns].copy()
    if data.empty:
        raise ValueError("转化后的数据为空DataFrame")
    
    # 处理NaN值并验证数据量
    data = data.dropna()
    if len(data) < 2:
        raise ValueError(f"有效样本不足（需要至少2个，实际：{len(data)}）")
    
    # PCA配置处理
    components = len(columns) if n_components is None else n_components
    if components < 1:
        return pd.Series([0]*len(columns), index=columns, name="PCA_Weight")

    # 执行PCA计算
    pca = PCA(n_components=min(components, len(data)))
    pca.fit(data)

    # 权重计算 - 使用单次流程提高效率
    loadings = pca.components_  # 形状(n_components, n_features)
    explained_var = pca.explained_variance_ratio_  # 形状(n_components,)

    # 验证有效组件数
    if components > len(explained_var):
        components = min(components, len(explained_var))
        pca = PCA(n_components=components)
        pca.fit(data)
        loadings = pca.components_
        explained_var = pca.explained_variance_ratio_

    # 计算平均特征载荷作为权重
    weights = np.abs(loadings).mean(axis=0)
    weights = weights / weights.sum()  # 归一化

    # 创建权重结果
    result = pd.Series(weights, index=columns, name="PCA_Weight")
    return result
